//
// Created by Gao Shihao on 2023/8/7.
//

#ifndef PLC2LLVM_TYPE_H
#define PLC2LLVM_TYPE_H

#include <utility>
#include <vector>
#include <variant>
#include <string>
#include "plc2llvm/Semantic/TypeMismatch.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/IRBuilder.h"

namespace plcst {
    using i32 = int32_t;
    using i64 = int64_t;
    using char8 = char8_t;
    using char16 = char16_t;

    enum class ConvertionSignal { //类型转换的返回信号
        IMPLICIT_CONVERSION, // 可以隐式转换
        EXPLICIT_CONVERSION, // 需要显式转换
        ERROR_CONVERSION // 不能转换
    };

    enum class TypeKind {
        //basic date types
        // int
        SINT, INT, DINT, LINT, // unsigned
        USINT, UINT, UDINT, ULINT, // signed

        // real
        REAL, LREAL,

        // duration
        TIME, LTIME,

        // date
        DATE, LDATE, TOD, LTOD, DT, LDT,

        // chars
        STRING, WSTRING, // string,
        CHAR, WCHAR, // char

        // bit
        BOOL, BYTE, WORD, DWORD, LWORD,

        //user defined date types
        ENUM, SUBRANGE, ARRAY, REF,
        STRUCT, DIRECTLY_DERIVED,
        INTERFACE,

        // other declaration
        FC, METHOD,

        // POU
        FUNCTION, FB, CLASS, PROGRAM, NAMESPACE,
    };

    enum class TypeCategory {
        ElementaryTypes,    // 基本类型
        GenericType,        // 通用类型
        UserDefinedType,    // 用户自定义类型
        POUType,            // 程序组织单元类型
    };

   enum class ExpressionOperator{
        POSITIVE, NEGATIVE, NOT, // +, -, !
        POWER, MULTI, DIV, MOD, ADD, MINUS, // **, *, /, %, +, -
        LESS, GREATER, LESSEQUAL, GREATEREQUAL, // >, <, <=, >=
        EQUAL, NOTEQUAL, // ==, !=
        AND, XOR, OR, //&, ^, |
    };

    class Type;

    /// @brief  signal: 0:normal, 1:warnning, 2:error, TODO:expand usage
    struct TypeMsg {
        std::shared_ptr<Type> t;
        int signal;
    };

    class Type {
    public:
        llvm::Type* llvmty;
        llvm::Value* initllvmValue;
    public:

        explicit Type(std::string&& name): typeName(std::move(name)) {
            typeObjID = typeObjCounter++;
        }

        explicit Type(Type* another) {
            typeObjID = typeObjCounter++;
            this->llvmty = another->llvmty;
            this->initllvmValue = another->initllvmValue;
            this->typeName = another->typeName;
        }

        virtual ~Type() = default;

        [[nodiscard]] virtual TypeKind getTypeKind() const = 0;

        [[nodiscard]] virtual TypeCategory classify() const = 0;

        void setName(std::string&& name);

        [[nodiscard]] std::string_view getTypeName() const;

        virtual bool is_basic_type();

        virtual bool is_userDef_type();

        virtual bool is_POU_type();

        /// @brief dest = this
        virtual ConvertionSignal canConvertTo(std::shared_ptr<Type> dest) = 0;

        /// @brief this op another
        virtual TypeMsg typeSynthesisForBinaryOperator(ExpressionOperator op, std::shared_ptr<Type> another) = 0;

        /// @brief op this
        virtual TypeMsg typeSynthesisForUnaryOperator(ExpressionOperator op) = 0;

        [[nodiscard]] int getTypeID() const;

        std::string toString();

        virtual llvm::Value* castTo(std::shared_ptr<Type> destTy, llvm::Value* src, llvm::IRBuilder<>* builder) = 0;

        virtual std::shared_ptr<Type> clone() = 0;

    private:
        std::string typeName; // 类型名称
        int typeObjID = 0;  // 类型对象的编号
        static inline int typeObjCounter = 0;  // 类型对象计数器
    };





}

#endif //PLC2LLVM_TYPE_H
